package io.kiki.stack.netty.channelManager;

import io.kiki.stack.netty.config.Configs;
import io.kiki.stack.netty.util.AddressParser;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.lang.ref.SoftReference;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Setter
@Getter
public class Url implements java.io.Serializable {
    private static final long serialVersionUID = 1L;
    public static ConcurrentHashMap<String, SoftReference<Url>> originalUrlToUrlMap = new ConcurrentHashMap<String, SoftReference<Url>>();


    private final String originUrl;
    /**
     * ip, can be number format or hostname format
     */
    private String ip;
    /**
     * port, should be integer between (0, 65535]
     */
    private int port;

    private String id;

    private int connectTimeout;

    private int channelNum = Configs.DEFAULT_CONN_NUM_PER_URL;
    /**
     * URL agrs: whether need warm up Channel
     */
    private boolean connWarmup;
    /**
     * URL agrs: all parsed args of each originUrl
     */
    private Properties properties;


    protected Url(String originUrl) {
        this.originUrl = originUrl;
    }


    public Url(String ip, int port) {
        this(ip + AddressParser.COLON + port);
        this.ip = ip;
        this.port = port;
        this.id = this.originUrl;
    }


    public Url(String originUrl, String ip, int port) {
        this(originUrl);
        this.ip = ip;
        this.port = port;
        this.id = ip + AddressParser.COLON + port;
    }


    public Url(String originUrl, String ip, int port, Properties properties) {
        this(originUrl, ip, port);
        this.properties = properties;
    }

    public Url(String originUrl, String ip, int port, String id, Properties properties) {
        this(originUrl, ip, port);
        this.id = id;
        this.properties = properties;
    }


    public String getProperty(String key) {
        if (properties == null) {
            return null;
        }
        return properties.getProperty(key);
    }


    public void setConnectTimeout(int connectTimeout) {
        if (connectTimeout <= 0) {
            throw new IllegalArgumentException("Illegal value of Channel number [" + channelNum + "], must be a positive integer].");
        }
        this.connectTimeout = connectTimeout;
    }


    public void setChannelNum(int channelNum) {
        if (channelNum <= 0 || channelNum > Configs.MAX_CONN_NUM_PER_URL) {
            throw new IllegalArgumentException("Illegal value of Channel number [" + channelNum + "], must be an integer between [" + Configs.DEFAULT_CONN_NUM_PER_URL + ", " + Configs.MAX_CONN_NUM_PER_URL + "].");
        }
        this.channelNum = channelNum;
    }


    @Override
    public final boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        Url url = (Url) obj;
        return this.getOriginUrl().equals(url.getOriginUrl());
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((this.getOriginUrl() == null) ? 0 : this.getOriginUrl().hashCode());
        return result;
    }


    @Override
    protected void finalize() {
        try {
            originalUrlToUrlMap.remove(this.getOriginUrl());
        } catch (Exception e) {
            log.error("Exception occurred when do finalize for Url [{}].", this.getOriginUrl(), e);
        }
    }


}
